﻿Public Class MonthlyReportRecord
	Inherits MonthlyReportData
	Implements IDisposable

	Dim db As New CongregationDataContainerLocal

	'this speeds up processing of this report by eliminating recalculation where possible
	'if the calculation was done less than 1 second ago, it isn't redone.
	Private Shared lastCalTime As DateTime

	Public ReadOnly Property ReportMonth As DateTime
		Get
			'since the report month should always be after the first of the next month,
			'we subtract a month from now and go to the first of that month
			Return Month.AddMonths(-1).FirstOfTheMonth
		End Get
	End Property

	Dim _CBSTMSSMNumberOfMeetings As Integer?
	Public ReadOnly Property CBSTMSSMNumberOfMeetings As Integer
		Get
			If _CBSTMSSMNumberOfMeetings Is Nothing OrElse (Now.AddSeconds(-1)) > lastCalTime Then
				CalculateAttendance()
			End If

			Return _CBSTMSSMNumberOfMeetings
		End Get
	End Property

	Dim _CBSTMSSMTotalAttendanceForMonth As Integer?
	Public ReadOnly Property CBSTMSSMTotalAttendanceForMonth As Integer
		Get
			If _CBSTMSSMTotalAttendanceForMonth Is Nothing OrElse (Now.AddSeconds(-1)) > lastCalTime Then
				CalculateAttendance()
			End If

			Return _CBSTMSSMTotalAttendanceForMonth
		End Get
	End Property

	Public ReadOnly Property CBSTMSSMAverageAttendanceEachWeek As Decimal
		Get
			If _CBSTMSSMNumberOfMeetings Is Nothing OrElse _CBSTMSSMTotalAttendanceForMonth Is Nothing OrElse (Now.AddSeconds(-1)) > lastCalTime Then
				CalculateAttendance()
			End If

			Return If(_CBSTMSSMNumberOfMeetings = 0, 0, _CBSTMSSMTotalAttendanceForMonth / _CBSTMSSMNumberOfMeetings)
		End Get
	End Property

	Dim _PTWTNumberOfMeetings As Integer?
	Public ReadOnly Property PTWTNumberOfMeetings As Integer
		Get
			If _PTWTNumberOfMeetings Is Nothing OrElse (Now.AddSeconds(-1)) > lastCalTime Then
				CalculateAttendance()
			End If

			Return _PTWTNumberOfMeetings
		End Get
	End Property

	Dim _PTWTTotalAttendanceForMonth As Integer?
	Public ReadOnly Property PTWTTotalAttendanceForMonth As Integer
		Get
			If _PTWTTotalAttendanceForMonth Is Nothing OrElse (Now.AddSeconds(-1)) > lastCalTime Then
				CalculateAttendance()
			End If

			Return _PTWTTotalAttendanceForMonth
		End Get
	End Property

	Public ReadOnly Property PTWTAverageAttendanceEachWeek As Decimal
		Get
			If _PTWTNumberOfMeetings Is Nothing OrElse _PTWTTotalAttendanceForMonth Is Nothing OrElse (Now.AddSeconds(-1)) > lastCalTime Then
				CalculateAttendance()
			End If

			Return If(_PTWTNumberOfMeetings = 0, 0, _PTWTTotalAttendanceForMonth / _PTWTNumberOfMeetings)
		End Get
	End Property

	Private Sub CalculateAttendance()
		Using db As New CongregationDataContainerLocal
			Dim monthsAttendance = db.AttendanceDatas.Where(Function(x) x.ReportMonth = ReportMonth)

			Dim CBSTMSSMWeek1 = From attendance In monthsAttendance
			  Select Attendance = attendance.Week1CBSTMSSMAttendance,
			  attendance.MeetingLocation
			Dim CBSTMSSMWeek2 = From attendance In monthsAttendance
			  Select Attendance = attendance.Week2CBSTMSSMAttendance,
			  attendance.MeetingLocation
			Dim CBSTMSSMWeek3 = From attendance In monthsAttendance
			  Select Attendance = attendance.Week3CBSTMSSMAttendance,
			  attendance.MeetingLocation
			Dim CBSTMSSMWeek4 = From attendance In monthsAttendance
			  Select Attendance = attendance.Week4CBSTMSSMAttendance,
			  attendance.MeetingLocation
			Dim CBSTMSSMWeek5 = From attendance In monthsAttendance
			  Select Attendance = attendance.Week5CBSTMSSMAttendance,
			  attendance.MeetingLocation

			Dim PTWTWeek1 = From attendance In monthsAttendance
			  Select Attendance = attendance.Week1PTWTAttendance,
			  attendance.MeetingLocation
			Dim PTWTWeek2 = From attendance In monthsAttendance
			  Select Attendance = attendance.Week2PTWTAttendance,
			  attendance.MeetingLocation
			Dim PTWTWeek3 = From attendance In monthsAttendance
			  Select Attendance = attendance.Week3PTWTAttendance,
			  attendance.MeetingLocation
			Dim PTWTWeek4 = From attendance In monthsAttendance
			  Select Attendance = attendance.Week4PTWTAttendance,
			  attendance.MeetingLocation
			Dim PTWTWeek5 = From attendance In monthsAttendance
			  Select Attendance = attendance.Week5PTWTAttendance,
			  attendance.MeetingLocation

			If CBSTMSSMWeek5.Count > 0 AndAlso CBSTMSSMWeek5.Sum(Function(x) x.Attendance) > 0 Then
				_CBSTMSSMNumberOfMeetings = 5
			ElseIf CBSTMSSMWeek4.Count > 0 AndAlso CBSTMSSMWeek4.Sum(Function(x) x.Attendance) > 0 Then
				_CBSTMSSMNumberOfMeetings = 4
			ElseIf CBSTMSSMWeek3.Count > 0 AndAlso CBSTMSSMWeek3.Sum(Function(x) x.Attendance) > 0 Then
				_CBSTMSSMNumberOfMeetings = 3
			ElseIf CBSTMSSMWeek2.Count > 0 AndAlso CBSTMSSMWeek2.Sum(Function(x) x.Attendance) > 0 Then
				_CBSTMSSMNumberOfMeetings = 2
			ElseIf CBSTMSSMWeek1.Count > 0 AndAlso CBSTMSSMWeek1.Sum(Function(x) x.Attendance) > 0 Then
				_CBSTMSSMNumberOfMeetings = 1
			Else
				_CBSTMSSMNumberOfMeetings = 0
			End If

			If PTWTWeek5.Count > 0 AndAlso PTWTWeek5.Sum(Function(x) x.Attendance) > 0 Then
				_PTWTNumberOfMeetings = 5
			ElseIf PTWTWeek4.Count > 0 AndAlso PTWTWeek4.Sum(Function(x) x.Attendance) > 0 Then
				_PTWTNumberOfMeetings = 4
			ElseIf PTWTWeek3.Count > 0 AndAlso PTWTWeek3.Sum(Function(x) x.Attendance) > 0 Then
				_PTWTNumberOfMeetings = 3
			ElseIf PTWTWeek2.Count > 0 AndAlso PTWTWeek2.Sum(Function(x) x.Attendance) > 0 Then
				_PTWTNumberOfMeetings = 2
			ElseIf PTWTWeek1.Count > 0 AndAlso PTWTWeek1.Sum(Function(x) x.Attendance) > 0 Then
				_PTWTNumberOfMeetings = 1
			Else
				_PTWTNumberOfMeetings = 0
			End If

			_CBSTMSSMTotalAttendanceForMonth = If(CBSTMSSMWeek1.Count = 0, 0, CBSTMSSMWeek1.Sum(Function(x) x.Attendance))
			_CBSTMSSMTotalAttendanceForMonth += If(CBSTMSSMWeek2.Count = 0, 0, CBSTMSSMWeek2.Sum(Function(x) x.Attendance))
			_CBSTMSSMTotalAttendanceForMonth += If(CBSTMSSMWeek3.Count = 0, 0, CBSTMSSMWeek3.Sum(Function(x) x.Attendance))
			_CBSTMSSMTotalAttendanceForMonth += If(CBSTMSSMWeek4.Count = 0, 0, CBSTMSSMWeek4.Sum(Function(x) x.Attendance))
			_CBSTMSSMTotalAttendanceForMonth += If(CBSTMSSMWeek5.Count = 0, 0, CBSTMSSMWeek5.Sum(Function(x) x.Attendance))

			_PTWTTotalAttendanceForMonth = If(PTWTWeek1.Count = 0, 0, PTWTWeek1.Sum(Function(x) x.Attendance))
			_PTWTTotalAttendanceForMonth += If(PTWTWeek2.Count = 0, 0, PTWTWeek2.Sum(Function(x) x.Attendance))
			_PTWTTotalAttendanceForMonth += If(PTWTWeek3.Count = 0, 0, PTWTWeek3.Sum(Function(x) x.Attendance))
			_PTWTTotalAttendanceForMonth += If(PTWTWeek4.Count = 0, 0, PTWTWeek4.Sum(Function(x) x.Attendance))
			_PTWTTotalAttendanceForMonth += If(PTWTWeek5.Count = 0, 0, PTWTWeek5.Sum(Function(x) x.Attendance))

		End Using

		lastCalTime = Now
	End Sub

	Public Function Save(Optional MonthOfReport As DateTime? = Nothing) As Boolean

		Using db As New CongregationDataContainerLocal
			'check to see if the previous month is missing
			'if so, fail to save unless a specific month is specified
			If MonthOfReport Is Nothing AndAlso Not db.MonthlyReportDatas.ToList.Any(Function(x) x.Month.FirstOfTheMonth = ReportMonth.AddMonths(-1).FirstOfTheMonth) Then
				Return False
			End If
			If MonthOfReport Is Nothing Then
				MonthOfReport = ReportMonth
			End If

			'don't save the report if there is already one for the month
			If Not db.MonthlyReportDatas.ToList.Any(Function(x) x.Month.FirstOfTheMonth = MonthOfReport) Then

				Dim newMonthlyReport As MonthlyReportData

				newMonthlyReport = MonthlyReportData.CreateMonthlyReportData(Date.UtcNow, PublisherCount, PublisherBooks, PublisherBrochures, PublisherHours, PublisherMagazines, PublisherReturnVisits, PublisherStudies, AuxPioneerCount, AuxPioneerBooks, AuxPioneerBrochures, AuxPioneerHours, AuxPioneerMagazines, AuxPioneerReturnVisits, AuxPioneerStudies, PioneerCount, PioneerBooks, PioneerBrochures, PioneerHours, PioneerMagazines, PioneerReturnVisits, PioneerStudies)
				db.MonthlyReportDatas.AddObject(newMonthlyReport)

				db.SaveChanges()

				'set this to nothing so that we can get a clean, not cached, date
				ConfigPropertyKeys.lastCalTime = Nothing

				Return True
			End If

			Return False
		End Using
	End Function

	Public Sub New(BranchReport As VM_BranchReport)
		MyBase.New(BranchReport)
	End Sub

	Public Sub New(MonthToRetrieve As DateTime)
		'get record from database given a month
		MonthToRetrieve = MonthToRetrieve.FirstOfTheMonth
		Using db As New CongregationDataContainerLocal
			'.ToList must be called first because extension methods can't be run on the SQL engine
			Dim reportRecord = db.MonthlyReportDatas.ToList.SingleOrDefault(Function(x) x.Month.FirstOfTheMonth = MonthToRetrieve)

			If reportRecord IsNot Nothing Then
				With reportRecord

					Me.Month = .Month
					Me.AuxPioneerBooks = .AuxPioneerBooks
					Me.AuxPioneerBrochures = .AuxPioneerBrochures
					Me.AuxPioneerCount = .AuxPioneerCount
					Me.AuxPioneerHours = .AuxPioneerHours
					Me.AuxPioneerMagazines = .AuxPioneerMagazines
					Me.AuxPioneerReturnVisits = .AuxPioneerReturnVisits
					Me.AuxPioneerStudies = .AuxPioneerStudies
					Me.PioneerBooks = .PioneerBooks
					Me.PioneerBrochures = .PioneerBrochures
					Me.PioneerCount = .PioneerCount
					Me.PioneerHours = .PioneerHours
					Me.PioneerMagazines = .PioneerMagazines
					Me.PioneerReturnVisits = .PioneerReturnVisits
					Me.PioneerStudies = .PioneerStudies
					Me.PublisherBooks = .PublisherBooks
					Me.PublisherBrochures = .PublisherBrochures
					Me.PublisherCount = .PublisherCount
					Me.PublisherHours = .PublisherHours
					Me.PublisherMagazines = .PublisherMagazines
					Me.PublisherReturnVisits = .PublisherReturnVisits
					Me.PublisherStudies = .PublisherStudies

				End With
			Else
				Throw New ArgumentOutOfRangeException("Month not on record.")
			End If

		End Using

	End Sub

#Region "IDisposable Support"
	Private disposedValue As Boolean ' To detect redundant calls

	' IDisposable
	Protected Overridable Sub Dispose(disposing As Boolean)
		If Not Me.disposedValue Then
			If disposing Then
				' TODO: dispose managed state (managed objects).
				db.Dispose()
			End If

			' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
			' TODO: set large fields to null.
		End If
		Me.disposedValue = True
	End Sub

	' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
	'Protected Overrides Sub Finalize()
	'    ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
	'    Dispose(False)
	'    MyBase.Finalize()
	'End Sub

	' This code added by Visual Basic to correctly implement the disposable pattern.
	Public Sub Dispose() Implements IDisposable.Dispose
		' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
		Dispose(True)
		GC.SuppressFinalize(Me)
	End Sub
#End Region

End Class
